package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"testing"
	"time"
)

func main()  {
	//池(Pool)是Pool模式的并发安全实现
	//Pool模式是一种创建和提供可供使用的固定数量实例或Pool实例的方法
	//Pool的主接口方法是它的Get方法
	//示例：
	myPool := &sync.Pool{
		new: func() interface{} {
			fmt.Println("Creating new instance.")
			return struct{}{}
		},
	}
	myPool.Get()   //调用Pool的get方法，调用将执行Pool中定义的new函数，因为实例还没有实例化
	instance := myPool.Get()   //同上
	myPool.Put(instance)   //将先前检索到的实例放在池中，增加了实例可用数量
	myPool.Get()   //执行此调用，将重用以前分配的实例并将其放回池中，New将不会被调用

	var numCalcsCreated int
	calcPool := &sync.Pool {
		New: func() interface{} {
			numCalcsCreated += 1
			mem := make([]byte,1024)
			return &mem       //正在存储bytes切片的地址
		},
	}

	//使用4KB初始化pool
	calcPool.Put(calcPool.New())
	calcPool.Put(calcPool.New())
	calcPool.Put(calcPool.New())
	calcPool.Put(calcPool.New())

	const numWorkers = 1024*1024
	var wg sync.WaitGroup
	wg.Add(numWorkers)
	for i := numWorkers; i > 0;i -- {
		go func() {
			defer wg.Done()
			mem := calcPool.Get().(*[]byte)       //断言类型是一个指向bytes切片的指针
			defer calcPool.Put(mem)

			//做一些有趣的假设，但是很快就会用这个内存完成
		}()
	}

	wg.Wait()
	fmt.Printf("%d calculators were created.",mumCalcsCreated)



}

//用Pool来尽可能快地将预先分配地对象缓存加载启动
//不是试图通过该限制创建地对象地数量来节省主机地内存，而是通过提前加载获取引用到另一个对象所需地时间，来节省消费者地时间
//示例对比：
//创建一个模拟创建到服务的连接的函数，连接花费很长时间
func connectToService() interface{} {
	time.Sleep(1*time.Second)
	return struct{}{}
}

//编写一个网络处理程序，为每一个请求都打开一个新的连接
func startNetworkDaemon() *sync.WaitGroup {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		server,err := net.Listen("tcp","localhost:8080")
		if err != nul {
			log.Fatalf("cannot listen: %v",err)
		}
		defer server.Close()

		wg.Done()

		for {
			conn,err := server.Accept()
			if err != nil{
				log.Printf("cannot accept connection: %v",err)
				continue
			}
			connectToService()
			fmt.Fprintln(conn,"")
			conn.Close()
		}
	}()
	return &wg
}

//基准及测试方法
func init() {
	daemonStarted := startNetworkDaemon()
	daemonStarted.Wait()
}

func BenchmarkNetworkRequest(b *testing.B) {
	for i:=0;i<b.N;i++ {
		conn,err := net.Dial("tcp","localhost:8080")
		if err!=nil {
			b.Fatalf("cannot dial host: %v",err)
		}
		if _,err := ioutil.ReadAll(conn);err != nil {
			b.Fatalf("cannot read: %v",err)
		}
		conn.Close()
	}
}

//测试
//go test -benchtime=10s -bench=.



使用sync.Pool来改进上面的虚拟服务
func warmServiceConnCache() *sync.Pool {
	p := &sync.Pool {
		New: connectToService,
	}
	for i:=0;i<10;i++ {
		p.Put(p.New())
	}
	return p
}

func startNetworkDaemon() *sync.WaitGroup {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		connPool := warmServiceConnCache()
		server,err := net.Listen("tcp","localhost:8080")
		if err != nil {
			log.Fatalf("cannot listen: %v",err)
		}
		defer server.Close()

		wg.Done()

		for {
			conn,err := server.Accept()
			if err != nil {
				log.Printf("cannot accept connection: %v",err)
				continue
			}
			svcConn := connPool.Get()
			fmt.Fprintln(conn,"")
			connPool.Put(svcConn)
			conn.Close()
		}
	}()
	return &wg
}
//进行基础测试
//go test -benchtime=10s -bench=.



//并发进程需要请求一个对象，但是在实例化之后很快的处理他们，或者在这些对象的构造可能会对内存产生负面影响，这时最好使用Pool设计模式
//如果你使用 Pool 代码所需要的东西不是大概同质的，那么从 Pool 转化检索到所需要的内容的时间可能比重新实例化内容要花费的时间更多，
//例如，程序需要随机和可变长度的切片，不建议使用Pool
//
//使用Pool时注意事项：
//1、当实例化sync.Pool，使用new方法创建一个成员变量，在调用时是线程安全的
//2、当收到一个来自Get的实例时，不要对所接收的对象的状态做出任何假设
//3、当用完了一个从Pool中取出的对象时，一定调用Put，否则，Pool就无法复用这个实例，通常情况，是用defer完成的
//4、Pool内的分布必须大致均匀













